﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartMathLibrary.Space.CoreTypes;

namespace SmartMathLibrary.Space.QueryEngine
{
    /// <summary>
    /// Defines the integral between option of a query.
    /// </summary>
    public enum IntegralBetweenOption
    {
        /// <summary>
        /// Only the lower border value of the integrals interests for the query.
        /// </summary>
        OnlyLowerBorder = 0,

        /// <summary>
        /// Only the upper border value of the integrals interests for the query.
        /// </summary>
        OnlyUpperBorder = 1,

        /// <summary>
        /// The lower and upper border value of the integrals interests for the query.
        /// </summary>
        LowerAndUpperBorder = 3
    }

    /// <summary>
    /// This class represents a query, which extracts all integrals out of a space which lies between 
    /// the an specified interval. 
    /// </summary>
    [Serializable]
    public class IntegralBetweenQuery : AbstractSpaceQuery
    {
        /// <summary>
        /// The start interval value of the query.
        /// </summary>
        private double start;

        /// <summary>
        /// The end interval value of the query.
        /// </summary>
        private double end;

        /// <summary>
        /// Defines the integral between option of the query.
        /// </summary>
        private IntegralBetweenOption betweenOption;

        /// <summary>
        /// Defines the lower between option of the query.
        /// </summary>
        private LowerBetweenOption lowerBetweenOption;

        /// <summary>
        /// Defines the upper between option of the query.
        /// </summary>
        private UpperBetweenOption upperBetweenOption;

        /// <summary>
        /// Initializes a new instance of the <see cref="IntegralBetweenQuery"/> class.
        /// </summary>
        /// <param name="start">The start interval value of the query.</param>
        /// <param name="end">The end interval value of the query.</param>
        public IntegralBetweenQuery(double start, double end)
            : base()
        {
            this.start = start;
            this.end = end;
            this.betweenOption = IntegralBetweenOption.LowerAndUpperBorder;
            this.lowerBetweenOption = LowerBetweenOption.GreaterThanOrEqual;
            this.upperBetweenOption = QueryEngine.UpperBetweenOption.LowerThanOrEqual;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="IntegralBetweenQuery"/> class.
        /// </summary>
        /// <param name="start">The start interval value of the query.</param>
        /// <param name="end">The end interval value of the query.</param>
        /// <param name="betweenOption">Defines the integral between option of the query.</param>
        public IntegralBetweenQuery(double start, double end, IntegralBetweenOption betweenOption)
            : base()
        {
            this.start = start;
            this.end = end;
            this.betweenOption = betweenOption;
            this.lowerBetweenOption = LowerBetweenOption.GreaterThanOrEqual;
            this.upperBetweenOption = QueryEngine.UpperBetweenOption.LowerThanOrEqual;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="IntegralBetweenQuery"/> class.
        /// </summary>
        /// <param name="start">The start interval value of the query.</param>
        /// <param name="lowerBorderBetweenOption">The lower border between option.</param>
        /// <param name="end">The end interval value of the query.</param>
        /// <param name="upperBorderBetweenOption">The upper border between option.</param>
        /// <param name="betweenOption">Defines the integral between option of the query.</param>
        public IntegralBetweenQuery(double start, LowerBetweenOption lowerBorderBetweenOption, double end,
                                    UpperBetweenOption upperBorderBetweenOption, IntegralBetweenOption betweenOption)
            : base()
        {
            this.start = start;
            this.end = end;
            this.betweenOption = betweenOption;
            this.lowerBetweenOption = lowerBorderBetweenOption;
            this.upperBetweenOption = upperBorderBetweenOption;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="IntegralBetweenQuery"/> class.
        /// </summary>
        /// <param name="start">The start interval value of the query.</param>
        /// <param name="lowerBorderBetweenOption">The lower border between option.</param>
        /// <param name="end">The end interval value of the query.</param>
        /// <param name="upperBorderBetweenOption">The upper border between option.</param>
        /// <param name="betweenOption">Defines the integral between option of the query.</param>
        /// <param name="status">The object status criteria.</param>
        public IntegralBetweenQuery(double start, LowerBetweenOption lowerBorderBetweenOption, double end,
                                    UpperBetweenOption upperBorderBetweenOption, IntegralBetweenOption betweenOption,
                                    SpaceObjectStatus status)
            : base(status)
        {
            this.start = start;
            this.end = end;
            this.betweenOption = betweenOption;
            this.lowerBetweenOption = lowerBorderBetweenOption;
            this.upperBetweenOption = upperBorderBetweenOption;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="IntegralBetweenQuery"/> class.
        /// </summary>
        /// <param name="start">The start interval value of the query.</param>
        /// <param name="lowerBorderBetweenOption">The lower border between option.</param>
        /// <param name="end">The end interval value of the query.</param>
        /// <param name="upperBorderBetweenOption">The upper border between option.</param>
        /// <param name="betweenOption">Defines the integral between option of the query.</param>
        /// <param name="limit">The maximum number of query results.</param>
        public IntegralBetweenQuery(double start, LowerBetweenOption lowerBorderBetweenOption, double end,
                                    UpperBetweenOption upperBorderBetweenOption, IntegralBetweenOption betweenOption,
                                    int limit)
            : base(limit)
        {
            this.start = start;
            this.end = end;
            this.betweenOption = betweenOption;
            this.lowerBetweenOption = lowerBorderBetweenOption;
            this.upperBetweenOption = upperBorderBetweenOption;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="IntegralBetweenQuery"/> class.
        /// </summary>
        /// <param name="start">The start interval value of the query.</param>
        /// <param name="lowerBorderBetweenOption">The lower border between option.</param>
        /// <param name="end">The end interval value of the query.</param>
        /// <param name="upperBorderBetweenOption">The upper border between option.</param>
        /// <param name="betweenOption">Defines the integral between option of the query.</param>
        /// <param name="limit">The maximum number of query results.</param>
        /// <param name="status">The object status criteria.</param>
        public IntegralBetweenQuery(double start, LowerBetweenOption lowerBorderBetweenOption, double end,
                                    UpperBetweenOption upperBorderBetweenOption, IntegralBetweenOption betweenOption,
                                    int limit, SpaceObjectStatus status)
            : base(limit, status)
        {
            this.start = start;
            this.end = end;
            this.betweenOption = betweenOption;
            this.lowerBetweenOption = lowerBorderBetweenOption;
            this.upperBetweenOption = upperBorderBetweenOption;
        }

        /// <summary>
        /// Gets or sets the start interval value of the query.
        /// </summary>
        /// <value>The start interval value of the query.</value>
        public double Start
        {
            get { return this.start; }
            set { this.start = value; }
        }

        /// <summary>
        /// Gets or sets the end interval value of the query.
        /// </summary>
        /// <value>The end interval value of the query.</value>
        public double End
        {
            get { return this.end; }
            set { this.end = value; }
        }

        /// <summary>
        /// Gets or sets the integral between option of the query.
        /// </summary>
        /// <value>The integral between option of the query.</value>
        public IntegralBetweenOption BetweenOption
        {
            get { return this.betweenOption; }
            set { this.betweenOption = value; }
        }

        /// <summary>
        /// Gets or sets the lower between option of the query.
        /// </summary>
        /// <value>The lower between option of the query.</value>
        public LowerBetweenOption LowerBetweenOption
        {
            get { return this.lowerBetweenOption; }
            set { this.lowerBetweenOption = value; }
        }

        /// <summary>
        /// Gets or sets the upper between option of the query.
        /// </summary>
        /// <value>The upper between option of the query.</value>
        public UpperBetweenOption UpperBetweenOption
        {
            get { return this.upperBetweenOption; }
            set { this.upperBetweenOption = value; }
        }

        /// <summary>
        /// Defines the query criteria of the specified query.
        /// </summary>
        /// <param name="item">The query source items.</param>
        /// <returns>
        /// The space ID fulfills the query criteria the method returns true otherwise, false.
        /// </returns>
        public override bool QueryDefinition(ISpaceEntry item)
        {
            if (item is SpaceIntegral)
            {
                SpaceIntegral check = (SpaceIntegral) item;

                if (this.betweenOption == IntegralBetweenOption.OnlyLowerBorder)
                {
                    if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThan) &&
                        (this.upperBetweenOption == UpperBetweenOption.LowerThan))
                    {
                        return ((this.start < check.A) && (check.A < this.end));
                    }
                    else if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThanOrEqual) &&
                             (this.upperBetweenOption == UpperBetweenOption.LowerThan))
                    {
                        return ((this.start <= check.A) && (check.A < this.end));
                    }
                    else if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThan) &&
                             (this.upperBetweenOption == UpperBetweenOption.LowerThanOrEqual))
                    {
                        return ((this.start < check.A) && (check.A <= this.end));
                    }
                    else
                    {
                        return ((this.start <= check.A) && (check.A <= this.end));
                    }
                }
                else if (this.betweenOption == IntegralBetweenOption.OnlyUpperBorder)
                {
                    if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThan) &&
                        (this.upperBetweenOption == UpperBetweenOption.LowerThan))
                    {
                        return ((this.start < check.B) && (check.B < this.end));
                    }
                    else if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThanOrEqual) &&
                             (this.upperBetweenOption == UpperBetweenOption.LowerThan))
                    {
                        return ((this.start <= check.B) && (check.B < this.end));
                    }
                    else if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThan) &&
                             (this.upperBetweenOption == UpperBetweenOption.LowerThanOrEqual))
                    {
                        return ((this.start < check.B) && (check.B <= this.end));
                    }
                    else
                    {
                        return ((this.start <= check.B) && (check.B <= this.end));
                    }
                }
                else
                {
                    if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThan) &&
                        (this.upperBetweenOption == UpperBetweenOption.LowerThan))
                    {
                        return ((this.start < check.A) && (check.A < this.end) && (this.start < check.B) &&
                                (check.B < this.end));
                    }
                    else if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThanOrEqual) &&
                             (this.upperBetweenOption == UpperBetweenOption.LowerThan))
                    {
                        return ((this.start <= check.A) && (check.A < this.end) && (this.start <= check.B) &&
                                (check.B < this.end));
                    }
                    else if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThan) &&
                             (this.upperBetweenOption == UpperBetweenOption.LowerThanOrEqual))
                    {
                        return ((this.start < check.A) && (check.A <= this.end) && (this.start < check.B) &&
                                (check.B <= this.end));
                    }
                    else
                    {
                        return ((this.start <= check.A) && (check.A <= this.end) && (this.start <= check.B) &&
                                (check.B <= this.end));
                    }
                }
            }

            return false;
        }
    }
}